//
// Created by Never on 2019/10/27.
//
#include "ConfigurableEventListener.h"
#include <algorithm>
#include "RandomNumber.h"

class TestRandomNumber: public testing::Test{
public:
    RandomNumber randomNumber;
    TestRandomNumber(): randomNumber(){};
};

TEST_F(TestRandomNumber, GenerateRandomInterger)
{
    int experimentTime = 10000;
    int intervalNum = 10;
    double dif = 0.1;

    vector<int> intervals(intervalNum, 0);
    for(int i = 0; i < experimentTime; i++)
    {
        auto number = randomNumber.getRandomNumber(0, intervalNum - 1);
        intervals[number] += 1;
    }
    for (int i = 0; i < intervalNum; i++)
        ASSERT_TRUE(intervals[i] <= experimentTime / intervalNum * (1 + dif) and
                    intervals[i] >= experimentTime / intervalNum * (1 - dif));
}

TEST_F(TestRandomNumber, GenerateRandomRealNumber)
{
    int experimentTime = 10000;
    int intervalNum = 10;
    double dif = 0.1;

    vector<int> intervals(intervalNum, 0);
    for(int i = 0; i < experimentTime; i++)
    {
        auto number = randomNumber.getRandomNumber(0.0, 1.0);
        intervals[int(number*intervalNum)] += 1;
    }
    for (int i = 0; i < intervalNum; i++)
        ASSERT_TRUE(intervals[i] <= experimentTime / intervalNum * (1 + dif) and
                    intervals[i] >= experimentTime / intervalNum * (1 - dif));
}

TEST_F(TestRandomNumber, GenerateNonDuplicatedRandomNumbers)
{
    int lowerBound = 0;
    int upperBound = 99;
    int numOfRandomNum = 3;
    vector<int> countOfNum(upperBound-lowerBound, 0);

    vector<int> res(numOfRandomNum);
    randomNumber.nonDuplicatedRandomNumbers(lowerBound, upperBound, numOfRandomNum, res);

    ASSERT_EQ(numOfRandomNum, res.size());
    for(auto num: res)
    {
        ASSERT_TRUE(num <= upperBound and num >= lowerBound and countOfNum[num] == 0);
        countOfNum[num] += 1;
    }
}
TEST_F(TestRandomNumber, PickKElementsFromVectorAndPutThemInFirstKPositionRandomly)
{
    vector<int> originElements = {5, 3, 2, 1, 7, 6};
    vector<int> copyElements = originElements;

    randomNumber.pickKEleAndPutThemInFirstKPosRandomly(copyElements, 3);

    ASSERT_EQ(copyElements.size(), originElements.size());
    for(int i = 0; i < originElements.size(); i++)
        ASSERT_TRUE(find(copyElements.begin(), copyElements.end(), originElements[i]) != copyElements.end());
}
TEST_F(TestRandomNumber, ArrangeElementsOfInterval)
{
    vector<int> originElements = {5, 3, 2, 1, 7, 6};
    vector<int> copyElements = originElements;
    int difTime = 0;

    for(int time = 0; time < 100; time++)
    {
        randomNumber.arrangeElementsOfInterval(copyElements, 2, 4);
        ASSERT_EQ(copyElements.size(), originElements.size());
        for(int i = 0; i < 2; i++)
            ASSERT_EQ(originElements[i], copyElements[i]);
        if(copyElements[2] == originElements[2] and copyElements[3] == originElements[3]);
        else if(copyElements[2] == originElements[3] and copyElements[3] == originElements[2])
            difTime++;
        else
        {
            std::cerr << "wrong arrangement" << endl;
            difTime = 0;
        }
        for(int i = 4; i < originElements.size(); i++)
            ASSERT_EQ(originElements[i], copyElements[i]);
    }
    ASSERT_TRUE(difTime>0);
}
TEST_F(TestRandomNumber, IsCurEleChosen)
{
    ASSERT_TRUE(randomNumber.isCurEleChosen(1, 1));

    int chooseTime = 0;
    int totalTime = 100;
    for(int i = 0; i < totalTime; i++)
    {
        if(randomNumber.isCurEleChosen(1, 2))
            chooseTime++;
    }

    double chooseRatio = 1.0 * chooseTime / totalTime;

    ASSERT_TRUE(chooseRatio < 0.6 and chooseRatio > 0.4)<<chooseRatio;
}

TEST_F(TestRandomNumber, CopyConstructor)
{
    RandomNumber anotherRandomNumber(randomNumber);
    for(int i = 0; i < 10; i++)
        ASSERT_EQ(anotherRandomNumber.getRandomNumber(0, 10), randomNumber.getRandomNumber(0, 10));
}

TEST_F(TestRandomNumber, GenerateSameNumbersWithSameSeed){
    RandomNumber randomNumberWithSeed1(100);
    RandomNumber randomNumberWithSeed2(100);

    vector<int> randomNumbers1, randomNumbers2;

    for(int i = 0; i < 500; i++){
        randomNumbers1.emplace_back(randomNumberWithSeed1.getRandomNumber(0, 100));
        randomNumbers2.emplace_back(randomNumberWithSeed2.getRandomNumber(0, 100));
    }

    ASSERT_EQ(randomNumbers1, randomNumbers2);
}

GTEST_API_ int main(int argc, char** argv) {
    testing::InitGoogleTest(&argc, argv);
    initialGTest();
    return RUN_ALL_TESTS();
}
